import json
import logging
import time
from django.conf import settings
from datetime import datetime, timedelta
from preprocessor.preprocessor import EventPreprocessor
from .model_router import ACCOUNT_MODEL_ROUTER, DEVICE_MODEL_ROUTER
from redis import StrictRedis

_LOGGER = logging.getLogger(__name__)

REDIS_URL = settings.CP_REDIS_URL


class CPEventPreprocessor(EventPreprocessor):
    """ CP Preprocessor """
    def __init__(self, topic,
                 redis_conf_url=REDIS_URL,
                 db_name='default',
                 add_prefix=True):
        super().__init__(topic, add_prefix)
        self.account_table = self.get_account_table()
        self.device_table = self.get_device_table()
        self.redis = StrictRedis.from_url(redis_conf_url, charset="utf-8", decode_responses=True)
        self.db_name = db_name

    def get_account_table(self):
        return ACCOUNT_MODEL_ROUTER[self.topic]

    def get_device_table(self):
        return DEVICE_MODEL_ROUTER[self.topic]

    def _get_account_redis_key(self, user_id):
        return '{}:account:{}'.format(self.topic, user_id)

    def _get_device_redis_key(self, aid):
        return '{}:device:{}'.format(self.topic, aid)

    def _get_or_create_account(self, user_id, channel, ip, aid, registered_at, active_at):
        account, created = self.account_table.objects.using(self.db_name).get_or_create(
            user_id=user_id,
            defaults={
                'user_id': user_id,
                'channel': channel,
                'ip': ip,
                'aid': aid,
                'registered_at': registered_at,
                'active_at': active_at
            })
        return account, created

    def get_account(self, user_id, get_object=False):
        if get_object:
            return self.account_table.objects.using(self.db_name).filter(user_id=user_id).first()
        account_redis_key = self._get_account_redis_key(user_id)
        account = self.redis.get(account_redis_key)
        if not account:
            account = self.account_table.objects.using(self.db_name).filter(user_id=user_id).first()
            if not account:
                return
            account = account.__dict__
            account.pop('_state')
            account['registered_at'] = str(account['registered_at'])
            account['active_at'] = str(account['active_at'])
            self.redis.set(account_redis_key, json.dumps(account))
            self.redis.expire(account_redis_key, timedelta(minutes=240))
        else:
            account = json.loads(account)
        account['registered_at'] = datetime.strptime(account['registered_at'], '%Y-%m-%d %H:%M:%S')
        account['active_at'] = datetime.strptime(account['active_at'], '%Y-%m-%d %H:%M:%S')
        return account

    def get_device(self, aid, get_object=False):
        if get_object:
            return self.device_table.objects.using(self.db_name).filter(aid=aid).first()
        device_redis_key = self._get_device_redis_key(aid)
        device = self.redis.get(device_redis_key)
        if not device:
            device = self.device_table.objects.using(self.db_name).filter(aid=aid).first()
            if not device:
                return
            device = device.__dict__
            device.pop('_state')
            device['created_at'] = str(device['created_at'])
            device['active_at'] = str(device['active_at'])
            self.redis.set(device_redis_key, json.dumps(device))
            self.redis.expire(device_redis_key, timedelta(hours=1))
        else:
            device = json.loads(device)
        device['created_at'] = datetime.strptime(device['created_at'], '%Y-%m-%d %H:%M:%S')
        device['active_at'] = datetime.strptime(device['active_at'], '%Y-%m-%d %H:%M:%S')
        return device

    def _get_or_create_device(self, aid, channel, ip, created_at, active_at):
        device, created = self.device_table.objects.using(self.db_name).get_or_create(
            aid=aid,
            defaults={
                'channel': channel,
                'ip': ip,
                'aid': aid,
                'created_at': created_at,
                'active_at': active_at
            })
        return device, created

    def register_event_pre_process(self):
        registered_at = datetime.fromtimestamp(self.event_data['_event_time'])
        account, created = self._get_or_create_account(self.event_data['_user_id'], self.event_data['_chn'],
                                                       self.event_data['_ip'], self.event_data['_aid'],
                                                       registered_at, registered_at)
        return account, created

    def active_event_pre_process(self):
        active_time = datetime.fromtimestamp(self.event_data['_event_time'])
        device, created = self._get_or_create_device(self.event_data['_aid'], self.event_data['_chn'],
                                                     self.event_data['_ip'], active_time, active_time)
        return device, created

    def user_event_pre_process(self):
        account = self.get_account(self.event_data['_user_id'])
        event_datetime = datetime.fromtimestamp(self.event_data['_event_time'])
        event_date = event_datetime.replace(hour=0, minute=0, second=0, microsecond=0)
        if account:
            if self.event_data['_event_id'] != 'active':
                self.event_data['_chn'] = account['channel']
                self.event_data['_aid'] = account['aid']
                aid = account['aid']
            else:
                aid = self.event_data.get('_aid')
            self.event_data['_registered_at'] = int(time.mktime(account['registered_at'].timetuple()))
            register_date = account['registered_at'].replace(hour=0, minute=0, second=0, microsecond=0)
            self.event_data['_account_active_days'] = (event_date - register_date).days
            self.event_data['_is_new_user'] = 1 if event_date == register_date else 0
        else:
            aid = self.event_data.get('_aid')
        if aid:
            device = self.get_device(aid)
            if device:
                device_created_date = device['created_at'].replace(hour=0, minute=0, second=0, microsecond=0)
                self.event_data['_device_active_days'] = (event_date - device_created_date).days
                self.event_data['_is_new_device'] = 1 if event_date == device_created_date else 0

    def get_compatible_event_data(self):
        compatible_target = ['aid', 'ip', 'cvc', 'chn', 'user_id']
        compatible_event_data = {}
        for key, value in self.event_data.items():
            if key in compatible_target:
                compatible_event_data['_' + key] = value
            else:
                compatible_event_data[key] = value
        self.event_data = compatible_event_data

    def handle(self, event_data):
        self.event_data = event_data
        if event_data['_event_id'] == 'register':
            self.register_event_pre_process()
        elif event_data['_event_id'] == 'active':
            device, created_at = self.active_event_pre_process()
            if not created_at:
                device.active_at = datetime.fromtimestamp(self.event_data['_event_time'])
                device.save()
        else:
            self.add_event_prefix()
        if '_user_id' in self.event_data and self.event_data['_event_id'] != 'register':
            self.user_event_pre_process()
        return self.event_data
